package com.ycl.runnable;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ycl.controller.ServerController;
import com.ycl.mapper.BlackListMapper;
import com.ycl.mapper.ChatRecordMapper;
import com.ycl.mapper.UserMapper;
import com.ycl.mapper.VIPMapper;
import com.ycl.pojo.BlackList;
import com.ycl.pojo.ChatRecord;
import com.ycl.pojo.User;
import com.ycl.pojo.VIP;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ServerRunnable implements Runnable {

    // Mybatis的代理类
    private static SqlSession sqlSession;
    // 服务端的ServerSocket
    private ServerSocket serverSocket;
    //存放所有用户的socket信息
    private Map<String, Socket> socketMap = new HashMap<>();
    //数据库对象
    private UserMapper jdbc = new UserMapper();

    public ServerRunnable(ServerSocket serverSocket) {
        this.serverSocket = serverSocket;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Socket client = serverSocket.accept();
                //监听消息
                listenInfoFromClient(client);
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
    }

    /**
     * 监听客户端消息
     *
     * @param socket
     * @throws Exception
     */
    private void listenInfoFromClient(Socket socket) throws Exception {
        new Thread(() -> {
            while (true) {
                try {
                    InputStream inputStream = socket.getInputStream();
                    byte[] b = new byte[1024 * 1024];
                    inputStream.read(b);
                    String info = new String(b, StandardCharsets.UTF_8);
                    handleMenuByType(socket, JSON.parseObject(info));
                } catch (Exception e) {
                    e.printStackTrace();
                    break;
                }
            }
        }).start();
    }

    /**
     * 对客户端发来的消息处进行理
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void handleMenuByType(Socket socket, JSONObject info) throws Exception {
        String type = info.getString("type");
        // Vip 用户校验
        if (VIPUserVerify(socket, info, type)) return;
        if (null != type) {
            switch (Integer.parseInt(type)) {
                case 1 ->
                    // 注册
                        register(socket, info);
                case 2 ->
                    // 登录
                        login(socket, info);
                case 3 ->
                    // 忘记密码
                        forgetPassword(socket, info);
                case 4 ->
                    // 退出系统，关闭Socket
                        exit(socket, info);
                case 5 ->
                    // 检查是否可以私聊
                        privateChat(socket, info);
                case 6 ->
                    // 群聊
                        groupChat(socket);
                case 7 ->
                    // 在线人员
                        onLineNumber(socket);
                case 8 ->
                    // 修改密码
                        changePassword(socket, info);
                case 9 ->
                    // 注销账号
                        cancelAccount(socket, info);
                case 10 ->
                    // 退出
                        quit(socket, info);
                case 11 ->
                    // 历史记录
                        history(socket, info);
                case 12 ->
                    // 拉黑
                        intoBlackList(socket, info);
                case 13 ->
                    // 从拉黑列表里移除
                        outFoBlackList(socket, info);
                case 14 ->
                    // 办理VIP
                        handleVIP(socket, info);
                case 15 ->
                    // 查看拉黑列表
                        LookBlackList(socket, info);
                case -1 ->
                    // 发送私聊消息
                        sendPrivateMessage(socket, info);
                case -2 ->
                    // 发送群聊消息
                        sendGroupMessage(info);
            }
        }
    }

    /**
     * 15 查看拉黑列表
     *
     * @param socket
     * @param info
     */
    private void LookBlackList(Socket socket, JSONObject info) {

        try {
            // 获得增强的 sqlSession
            SqlSession sqlSession = getSqlSession();
            //执行sql
            BlackListMapper blackListMapper = sqlSession.getMapper(BlackListMapper.class);
            List<BlackList> blackLists = blackListMapper.selectAll((String) info.get("from"));
            //sqlSession.close();
            String jsonBlackLists = JSONObject.toJSONString(blackLists);
            JSONObject m = new JSONObject();
            m.put("type", "15");
            m.put("text", jsonBlackLists);
            ServerController.sendInfo(socket, m);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 14 办理VIP
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void handleVIP(Socket socket, JSONObject info) throws Exception {
        // 获得增强的 sqlSession
        SqlSession sqlSession = getSqlSession();
        //执行sql
        VIPMapper vipMapper = sqlSession.getMapper(VIPMapper.class);
        VIP from = vipMapper.select((String) info.get("from"));
        JSONObject m = new JSONObject();
        m.put("type", "14");
        if (from == null) {
            vipMapper.add((String) info.get("from"), true);
            sqlSession.commit();
            //sqlSession.close();
            m.put("text", "VIP办理成功！");
        } else {
            m.put("text", "VIP办理失败,您已经是VIP用户,无需再次办理!");
        }
        ServerController.sendInfo(socket, m);
    }

    /**
     * 13 从拉黑列表里移除
     *
     * @param socket
     * @param info
     */
    private void outFoBlackList(Socket socket, JSONObject info) {
        JSONObject m = new JSONObject();
        int flag = 0;// 0 没有拉黑或被拉黑关系  1 将对方拉黑   2 对方将我拉黑
        try {
            flag = isBlackList(info);
            if (flag == 1) {
                // 获得增强的 sqlSession
                SqlSession sqlSession = getSqlSession();
                //执行sql
                BlackListMapper blackListMapper = sqlSession.getMapper(BlackListMapper.class);
                blackListMapper.delete((String) info.get("from"), (String) info.get("to"));
                sqlSession.commit();
                //sqlSession.close();
                m.put("text", "从拉黑列表移除成功!");
            } else if (flag == 2 || flag == 0) {
                m.put("text", "您还未将对方拉黑！");
            }
            m.put("type", "13");
            ServerController.sendInfo(socket, m);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }

    /**
     * 12 拉黑
     *
     * @param socket
     * @param info
     */
    private void intoBlackList(Socket socket, JSONObject info) {
        try {
            JSONObject m = new JSONObject();
            int flag = isBlackList(info);// 0 没有拉黑或被拉黑关系  1 将对方拉黑   2 对方将我拉黑
            if (flag == 0 || flag == 2) {
                // 获得增强的 sqlSession
                SqlSession sqlSession = getSqlSession();
                //执行sql
                BlackListMapper blackListMapper = sqlSession.getMapper(BlackListMapper.class);
                BlackList blackList = new BlackList((String) info.get("from"), (String) info.get("to"), LocalDateTime.now());
                blackListMapper.add(blackList);
                sqlSession.commit();
                //sqlSession.close();
                m.put("text", "拉黑成功!");
            } else if (flag == 1) {
                m.put("text", "您已将对方拉黑，无需重复拉黑！");
            }
            m.put("type", "12");
            ServerController.sendInfo(socket, m);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 11 查看历史记录
     *
     * @param socket
     * @param info
     */
    private void history(Socket socket, JSONObject info) {
        try {
            //加载mybatis的核心配置文件，获取SqlSessionFactory
            SqlSession sqlSession = getSqlSession();
            //执行sql
            ChatRecordMapper chatRecordMapper = sqlSession.getMapper(ChatRecordMapper.class);
            String sender = (String) info.get("from");
            String recipient = (String) info.get("to");
            List<ChatRecord> chatRecords = chatRecordMapper.selectHistory(sender, recipient);
//            System.out.println(chatRecords);
//            System.out.println(chatRecords.get(0));
            //释放资源
            //sqlSession.close();
            //返回消息给客户端
//            System.out.println(chatRecords);
            JSONObject m = new JSONObject();
            m.put("text", chatRecords);
            m.put("type", "11");
            ServerController.sendInfo(socket, m);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 10 退出
     *
     * @param socket
     * @param info
     */
    private void quit(Socket socket, JSONObject info) {
        String username = info.getString("text");
        try {
            socketMap.remove(username);
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 9 注销账号
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void cancelAccount(Socket socket, JSONObject info) throws Exception {
        String username = info.getString("text");
        jdbc.delete(username);
        //移除登录数据
        socketMap.remove(username);
        JSONObject m = new JSONObject();
        m.put("type", "9");
        ServerController.sendInfo(socket, m);
    }

    /**
     * 8 修改密码
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void changePassword(Socket socket, JSONObject info) throws Exception {
        //转换成User类
        User user = JSON.parseObject(info.getString("text"), User.class);
        String update = jdbc.updatePassword(user);
        if (info.equals("ok")) {
            //移除登录数据
            socketMap.remove(user.getUsername());
        }
        JSONObject m = new JSONObject();
        m.put("text", update);
        m.put("type", "8");
        //返回消息给客户端
        ServerController.sendInfo(socket, m);
    }

    /**
     * 7 在线人数
     *
     * @param socket
     * @throws Exception
     */
    private void onLineNumber(Socket socket) throws Exception {
        StringBuilder builder = new StringBuilder(" 在线人员:");
        //map中取出用户信息，也就是在线用户
        for (String account : socketMap.keySet()) {
            builder.append(account);
            builder.append(" ");
        }
        JSONObject m = new JSONObject();
        m.put("text", builder);
        m.put("type", "7");
        ServerController.sendInfo(socket, m);
    }

    /**
     * 6 群聊
     *
     * @param socket
     * @throws Exception
     */
    private static void groupChat(Socket socket) throws Exception {
        JSONObject m = new JSONObject();
        m.put("type", "6");
        ServerController.sendInfo(socket, m);
    }

    /**
     * 5 私聊
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void privateChat(Socket socket, JSONObject info) throws Exception {
        String to = info.getString("to");
        JSONObject m = new JSONObject();
        m.put("type", "5");
        m.put("text", "");
        //判断map中是否有用户信息
        if (!socketMap.containsKey(to)) {
            m.put("text", "用户不在线");
        }
        int flag = isBlackList(info);
        if (flag == 1) {
            m.put("text", "您已经将对方拉黑！");
        } else if (flag == 2) {
            m.put("text", "对方已将您拉黑！");
        }
        m.put("to", to);

        ServerController.sendInfo(socket, m);
    }

    /**
     * 4 退出主页
     *
     * @param socket
     * @param info
     */
    private void exit(Socket socket, JSONObject info) {
        String user = info.getString("user");
        try {
            socketMap.remove(user);
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 3 忘记密码
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void forgetPassword(Socket socket, JSONObject info) throws Exception {
        User user = JSON.parseObject(info.getString("text"), User.class);
        String userInfo = jdbc.getUserInfo(user);
        JSONObject m = new JSONObject();
        m.put("text", userInfo);
        m.put("type", "3");
        ServerController.sendInfo(socket, m);
    }

    /**
     * 2 登录
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void login(Socket socket, JSONObject info) throws Exception {
        User user = JSON.parseObject(info.getString("text"), User.class);
        String str = "";
        if (null != socketMap.get(user.getUsername())) {
            str = "用户已登录，无需再次登录";
        } else {
            str = jdbc.login(user);
            if ("yes".equals(str)) {
                //把信息放到map中
                socketMap.put(user.getUsername(), socket);
            }
        }
        JSONObject m = new JSONObject();
        m.put("text", str);
        m.put("user", user.getUsername());
        m.put("type", "2");
        //返回消息给客户端
        ServerController.sendInfo(socket, m);
    }

    /**
     * 1 注册
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void register(Socket socket, JSONObject info) throws Exception {
        //转换成User类型
        User user = JSON.parseObject(info.getString("text"), User.class);
        //数据库新增用户
        int code = jdbc.add(user);
        JSONObject m = new JSONObject();
        if (code == 1) {
            m.put("text", "注册成功");
            m.put("type", "1");
        } else {
            m.put("text", "注册失败");
            m.put("type", "1");
        }
        //返回消息给客户端
        ServerController.sendInfo(socket, m);
    }

    /**
     * -2 发送群聊消息
     *
     * @param info
     * @throws Exception
     */
    private void sendGroupMessage(JSONObject info) throws Exception {
        //群聊，直接从map中取出所有的socket对象
        Iterator<Socket> iterator = socketMap.values().iterator();
        while (iterator.hasNext()) {
            //发送消息
            ServerController.sendInfo(iterator.next(), info);
        }
    }

    /**
     * -1 发送私聊消息
     *
     * @param socket
     * @param info
     * @throws Exception
     */
    private void sendPrivateMessage(Socket socket, JSONObject info) throws Exception {
        //获取私聊对象
        String to = info.getString("to");
        //判断是否有效用户对象
        JSONObject m = new JSONObject();
        m.put("type", "-1");
        //返回消息给客户端
        if (!socketMap.containsKey(to)) {
            m.put("text", "用户不在线");
            ServerController.sendInfo(socket, m);
            return;
        }

        //加载mybatis的核心配置文件，获取SqlSessionFactory
        SqlSession sqlSession = getSqlSession();
        //执行sql
        ChatRecordMapper chatRecordMapper = sqlSession.getMapper(ChatRecordMapper.class);
        ChatRecord chatRecord = new ChatRecord();
        chatRecord.setSender((String) info.get("from"));
        chatRecord.setRecipient((String) info.get("to"));
        chatRecord.setTime(LocalDateTime.now());
        chatRecord.setText((String) info.get("text"));
        chatRecordMapper.addMessage(chatRecord);
        sqlSession.commit();
        //释放资源
        //sqlSession.close();
        //发送消息
        ServerController.sendInfo(socketMap.get(to), info);
    }

    /**
     * 判断拉黑状况
     * 0 没有拉黑或被拉黑关系  1 将对方拉黑   2 对方将我拉黑
     *
     * @param info
     * @return
     * @throws IOException
     */
    private static int isBlackList(JSONObject info) throws IOException {// 0.无关系  1.已拉黑   2.反被拉黑
        //加载mybatis的核心配置文件，获取SqlSessionFactory
        SqlSession sqlSession = getSqlSession();
        //执行sql
        BlackListMapper blackListMapper = sqlSession.getMapper(BlackListMapper.class);
        BlackList select1 = blackListMapper.select((String) info.get("from"), (String) info.get("to"));
        if (select1 != null) {
            return 1;
        }
        BlackList select2 = blackListMapper.select((String) info.get("to"), (String) info.get("from"));
        if (select2 != null) {
            return 2;
        }
        //释放资源
        //sqlSession.close();
        return 0;
    }

    /**
     * 获得Mybatis的代理类
     *
     * @return
     * @throws IOException
     */
    private static SqlSession getSqlSession() throws IOException {
        if (sqlSession == null) {
            //加载mybatis的核心配置文件，获取SqlSessionFactory
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //获取SqlSession对象，用它来执行sql
            sqlSession = sqlSessionFactory.openSession();
            //sqlSession.close();
        }
        return sqlSession;
    }

    /**
     * Vip 用户校验
     *
     * @param socket
     * @param info
     * @param type
     * @return
     * @throws Exception
     */
    private static boolean VIPUserVerify(Socket socket, JSONObject info, String type) throws Exception {
        if (Integer.parseInt(type) >= 11 && Integer.parseInt(type) < 14) {
            SqlSession sqlSession = getSqlSession();
            VIPMapper vipMapper = sqlSession.getMapper(VIPMapper.class);
            VIP from = vipMapper.select((String) info.get("from"));
            //sqlSession.close();
            if (from == null || !from.isFlag()) {
                JSONObject m = new JSONObject();
                m.put("text", "这是VIP用户功能，您尚未成为VIP用户!");
                m.put("type", "14");
                ServerController.sendInfo(socket, m);
                return true;
            }
        }
        return false;
    }

}
